/**
 * JMBS: Java Micro Blogging System
 *
 * Copyright (C) 2012
 *
 * This program is free software: you can redistribute it and/or modify it under
 * the terms of the GNU General Public License as published by the Free Software
 * Foundation, either version 3 of the License, or (at your option) any later
 * version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY. See the GNU General Public License for more details. You should
 * have received a copy of the GNU General Public License along with this
 * program. If not, see <http://www.gnu.org/licenses/>.
 *
 * @author Younes CHEIKH http://cyounes.com
 * @author Benjamin Babic http://bbabic.com
 *
 */
package jmbs.server;

import jmbs.common.DAO;
import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
import java.sql.Connection;
import java.util.ArrayList;

import jmbs.common.ConnectionInformation;
import jmbs.common.Message;
import jmbs.common.Project;
import jmbs.common.RemoteRequests;
import jmbs.common.User;

public class Requests extends UnicastRemoteObject implements RemoteRequests {

	private Connection con = new Connect().getConnection();
	private static final long serialVersionUID = 5885886202424414094L;
	private boolean security = true;
	private ConnectionInformation ci;

	public Requests(boolean security, String connectionNumber)
			throws RemoteException {
		this.security = security;
		this.ci = new ConnectionInformation(connectionNumber);
	}

	/**
	 * Adds a message in the message owner's timeline.
	 * 
	 * @param m
	 *            - the message object to add
	 * @return the message id generated by the db
	 * @throws RemoteException
	 *             in case of Rmi error
	 */
	public int addMessage(Message m) throws RemoteException {
		MessageDAO mdao = new MessageDAO(con);
		int ret = mdao.addMessage(m);

		return ret;
	}

	/**
	 * Adds a message under the message owner name in the specified project
	 * timeline.
	 * 
	 * @param m
	 *            - the message object to add
	 * @param projectId
	 *            - the project id
	 * @return the message id generated by the db
	 * @throws RemoteException
	 *             in case of Rmi error
	 */
	public int addMessage(Message m, int projectId) throws RemoteException {
		MessageDAO mdao = new MessageDAO(con);
		int ret = mdao.addMessage(m, projectId);

		return ret;
	}

	/**
	 * Changes the mail of the user.<br>
	 * It requires his password.
	 * 
	 * @param userid
	 *            - the user id
	 * @param pass
	 *            - the user hashed pass
	 * @param mail
	 *            - the new mail
	 * @return true if change was sucessful
	 * @throws RemoteException
	 */
	public boolean changeMail(int userid, String pass, String mail)
			throws RemoteException {
		return new UserDAO(con).changeMail(userid, pass, mail);
	}

	/**
	 * Changes the user's password.<br>
	 * It requires the old password.
	 * 
	 * @param userid
	 *            - the user id
	 * @param oldPass
	 *            - the old password
	 * @param newPass
	 *            - the new password
	 * @return true if change was sucessful
	 * @throws RemoteException
	 */
	public boolean changePassword(int userid, String oldPass, String newPass)
			throws RemoteException {
		return new UserDAO(con).changePassword(userid, oldPass, newPass);
	}

	/**
	 * Changes user name
	 * 
	 * @param userid
	 *            - the user id
	 * @param name
	 *            - the user new name
	 * @return true if change was sucessful
	 * @throws RemoteException
	 */
	public boolean changName(int userid, String name) throws RemoteException {
		return new UserDAO(con).changeName(userid, name);
	}

	/**
	 * Changes user forename
	 * 
	 * @param userid
	 *            - the user id
	 * @param fname
	 *            - the user new forename
	 * @return true if change was sucessful
	 * @throws RemoteException
	 */
	public boolean changFname(int userid, String fname) throws RemoteException {
		return new UserDAO(con).changeFname(userid, fname);

	}

	/**
	 * Closes a project if the user is authorized to close it.
	 * 
	 * @param idUser
	 *            - the user who wants to close the project
	 * @param idProject
	 *            - the project to close
	 * @return true if the operation was sucessful
	 * @throws RemoteException
	 * @throws SecurityException
	 *             if user has not requiered access level
	 */
	public boolean closeProject(int idUser, int idProject)
			throws RemoteException, SecurityException {
		ProjectDAO pdao = new ProjectDAO(con);
		boolean b = false;
		if (pdao.isOwner(idUser, idProject)
				|| new SecurityDAO(con).isAccessLevelSufficiant(idUser,
						UserDAO.ADMIN_ACCESS_LEVEL)) {
			b = pdao.closeProject(idProject);
		} else {
			throw new SecurityException(
					"You are not autorized to delete this project.");
		}

		return b;
	}

	/**
	 * open a project if the user is authorized to close it.
	 * 
	 * @param idUser
	 *            - the user who wants to close the project
	 * @param idProject
	 *            - the project to close
	 * @return true if the operation was sucessful
	 * @throws RemoteException
	 * @throws SecurityException
	 *             if user has not requiered access level
	 */
	public boolean openProject(int idUser, int idProject)
			throws RemoteException, SecurityException {
		ProjectDAO pdao = new ProjectDAO(con);
		boolean b = false;
		if (pdao.isOwner(idUser, idProject)
				|| new SecurityDAO(con).isAccessLevelSufficiant(idUser,
						UserDAO.ADMIN_ACCESS_LEVEL)) {
			b = pdao.openProject(idProject);
		} else {
			throw new SecurityException(
					"You are not autorized to delete this project.");
		}

		return b;
	}

	public boolean close() throws RemoteException {
		ServerMonitor sm = ServerMonitor.getInstance();
		return sm.endConnection(this.ci.getConnectionNumber());
	}

	/**
	 * Connects a user to the server.
	 * 
	 * @param em
	 *            - the user email
	 * @param psw
	 *            - the user password
	 * @return the User object corresponding to the user or null if wrong pass
	 *         or email
	 * @throws RemoteException
	 * @throws SecurityException
	 *             if connection is not authorised by security manager
	 */
	public User connectUser(String em, String psw) throws RemoteException,
			SecurityException {
		User returnUser = new User();
		Security s = new SecurityDAO(new Connect().getConnection());
		ServerMonitor sm = ServerMonitor.getInstance();
		if (security) {
			if (s.isUserConnectionAttemptAuthorized(this.ci
					.getConnectionNumber())) { // Try to connect to an account
				// from an ip (security check)
				UserDAO udao = new UserDAO(con);
				User u = udao.getUser(em);

				if (u != null) {
					boolean b = udao.checkPassword(u.getId(), psw);
					if (b) { // if password is correct
						u.setFollows(udao.getFollowed(u));
						returnUser = u;
						sm.logIn(u.getId(), this.ci.getConnectionNumber()); // add
						// activated
						// account
						// to
						// server
						// Monitor
						this.ci.logIn(u.getId());
					}
				}

			} else {
				throw new SecurityException(
						"You're not authorized to connect to the server because your ip has made too many unsuccessfull login attemps.\n");
			}

		} else {
			UserDAO udao = new UserDAO(con);
			User u = udao.getUser(em);

			if (u != null) {
				boolean b = udao.checkPassword(u.getId(), psw);
				if (b) { // if password is correct
					u.setFollows(udao.getFollowed(u));
					returnUser = u;
				}
			}
		}
		return returnUser;
	}

	/**
	 * Creates a project under user's name if he is authorised to do so
	 * 
	 * @param name
	 *            - project name
	 * @param iduser
	 *            - user's id
	 * @return true if project is created
	 * @deprecated
	 * @throws SecurityException
	 *             if user is not allowed to create Project
	 */
	public boolean createProject(String name, int iduser)
			throws RemoteException, SecurityException {
		ProjectDAO pdao = new ProjectDAO(con);
		SecurityDAO sdao = new SecurityDAO(con);
		boolean b = false;

		if (sdao.isAccessLevelSufficiant(iduser, UserDAO.CREATE_ACCESS_LEVEL)) {
			b = pdao.createProject(name, iduser);
		} else {
			throw new SecurityException(
					"You are not authorized to create a project.\n "
							+ "Please contact your administator for further informations.");
		}

		return b;
	}

	/**
	 * Creates a project with all the options.
	 * 
	 * @param name
	 *            - project name
	 * @param iduser
	 *            - owner id
	 * @param activation
	 *            - 1 to activate
	 * @param edit
	 *            - true to enable message edditing by the owner
	 * @param supress
	 *            - true to enable messsage deleting by the owner
	 * @param privacy
	 *            - true to set it as a public project
	 * @return int - the created project id or -1
	 * @throws SecurityException
	 *             if user is not authorised to create the project
	 */
	public int createProject(String name, int iduser, int activation,
			boolean edit, boolean supress, boolean privacy)
			throws RemoteException, SecurityException {
		ProjectDAO pdao = new ProjectDAO(con);
		SecurityDAO sdao = new SecurityDAO(con);
		int ret = -1;

		if (sdao.isAccessLevelSufficiant(iduser, UserDAO.CREATE_ACCESS_LEVEL)) {
			ret = pdao.createProject(name, iduser, activation, edit, supress,
					privacy);
		} else {
			throw new SecurityException(
					"You are not authorized to create a project.\n "
							+ "Please contact your administator for further informations.");
		}

		return ret;
	}

	// TODO send a mail and check if used.
	/*
	 * (non-Javadoc)
	 * 
	 * @see jmbs.common.RemoteServer#createUser(jmbs.common.User,
	 * java.lang.String)
	 */
	public boolean createUser(User u, String hashedPassword)
			throws RemoteException {
		return new UserDAO(con).addUser(u, hashedPassword);
	}

	public boolean createUser(int userid, User u, String hashedPassword,
			int authlvl) throws RemoteException, SecurityException {
		return new UserDAO(con).addUser(userid, u, hashedPassword, authlvl);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see jmbs.common.RemoteServer#follow(int, int)
	 */
	public boolean follows(int idfollower, int idfollowed)
			throws RemoteException {
		UserDAO udao = new UserDAO(con);
		boolean rb = udao.follows(idfollower, idfollowed);

		return rb;
	}

	public ConnectionInformation getConnectionInformations() {
		return this.ci;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see jmbs.common.RemoteServer#getFollowers(jmbs.common.User)
	 */
	public ArrayList<User> getFollowers(User u) throws RemoteException {
		UserDAO udao = new UserDAO(con);
		ArrayList<User> ra = udao.getFollowers(u);

		return ra;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see jmbs.common.RemoteServer#getLatestTL(int, int)
	 */
	public ArrayList<Message> getLatestTL(int iduser, int idlastmessage,
			int maxMsg) throws RemoteException {
		MessageDAO mdao = new MessageDAO(con);
		ArrayList<Message> ra = mdao.getMessages(iduser, idlastmessage, maxMsg);

		return ra;
	}

	public ArrayList<Message> getLastetProjectTL(int iduser, int idlastmessage,
			int maxMsg, int idproject) throws RemoteException {
		ArrayList<Message> ra = new ArrayList<Message>();
		// if (new ProjectDAO(con).isUserInvolved(idproject, iduser)){
		ra = new MessageDAO(con).getMessages(iduser, idlastmessage, maxMsg,
				idproject);
		// }

		return ra;
	}

	public ArrayList<User> getProjectUsers(int idProject)
			throws RemoteException {
		ProjectDAO pdao = new ProjectDAO(con);
		return pdao.getUsers(idProject);
	}

	public ArrayList<Project> getUserProjects(int idUser)
			throws RemoteException {
		UserDAO udao = new UserDAO(con);
		return udao.getProjects(idUser);
	}

	public void logOut(int userid) throws RemoteException {
		ServerMonitor.getInstance().logOut(userid);
	}

	public boolean participate(int iduser, int idproject, int auth)
			throws RemoteException {
		UserDAO udao = new UserDAO(con);
		boolean ret = udao.participate(iduser, idproject, auth);

		return ret;
	}

	public boolean participate(int iduser, int idproject)
			throws RemoteException {
		return this
				.participate(iduser, idproject, UserDAO.DEFAULT_ACCESS_LEVEL);
	}

	/**
	 * This method returns all the projects where the project name is like the
	 * given parameter regardless to project privacy setting.\n It is strongly
	 * adviced not to use this method !
	 * 
	 * @deprecated
	 * @param likeName
	 * @return
	 * @throws RemoteException
	 */
	public ArrayList<Project> searchForProject(String likeName)
			throws RemoteException {
		ProjectDAO pdao = new ProjectDAO(con);
		ArrayList<Project> found = pdao.findProjects(likeName);

		return found;
	}

	/**
	 * This method returns all the projects a user a able to see regarding the
	 * project provacy settings and the user accesslevel where the project name
	 * is like the given parameter.
	 * 
	 * @param likeName
	 *            name of the project to search for
	 * @param userId
	 *            id of the researcher
	 * @return Array of found projects
	 * @throws RemoteException
	 */
	public ArrayList<Project> searchForProject(String likeName, int userId)
			throws RemoteException {
		ProjectDAO pdao = new ProjectDAO(con);
		ArrayList<Project> found = pdao.findProjects(likeName, userId);

		return found;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see jmbs.common.RemoteServer#searchFor(java.lang.String)
	 */
	public ArrayList<User> searchUser(String userName, int param)
			throws RemoteException {
		UserDAO udao = new UserDAO(con);

		if (param != DAO.BY_NAME && param != DAO.BY_FORNAME
				&& param != DAO.BY_BOTH) {
			param = DAO.BY_BOTH; // default value
		}
		ArrayList<User> u = udao.findUsers(userName, param);

		return u;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see jmbs.common.RemoteServer#unFollow(int, int)
	 */
	public boolean unFollow(int idfollower, int idfollowed)
			throws RemoteException {
		UserDAO udao = new UserDAO(con);
		boolean rb = udao.unFollow(idfollower, idfollowed);

		return rb;
	}

	public boolean unParticipate(int iduser, int idproject)
			throws RemoteException {
		UserDAO udao = new UserDAO(con);
		boolean ret = udao.unParticipate(iduser, idproject);

		return ret;
	}

	public ArrayList<Project> getOwnedProjects(int userid)
			throws RemoteException {
		return new UserDAO(con).getOwnedProjects(userid);
	}

	public Project findProject(String name) throws RemoteException {
		return new ProjectDAO(con).getProject(name);
	}

	public byte[] getPicture(int userId) throws RemoteException {
		return Picture.getUserPicture(userId);
	}

	public boolean setPicture(int userId, byte[] imageInByte)
			throws RemoteException {
		return Picture.setUserPicture(userId, imageInByte);
	}

	public boolean changeAvatar(int userId, byte[] imageInByte)
			throws RemoteException {
		return Picture.setUserPicture(userId, imageInByte);
	}
}
